//
// Created by yinlijun on 2017/9/30.
// 抽象的工作队列。
//

#include <pthread.h>
#include "work_item_queue.h"

typedef struct WorkItem {
    void* data;
    work_item_destroy_func destroy;
    struct WorkItem* next;
}WorkItem;

typedef struct WorkItemQueue {
    pthread_mutex_t locker;
    WorkItem* head;
    WorkItem* tail;
    int count;
}WorkItemQueue;

WorkItemQueue* work_item_queue_create() {
    WorkItemQueue* thiz = (WorkItemQueue *)malloc(sizeof(WorkItemQueue));
    pthread_mutex_init(&thiz->locker, NULL);
    thiz->head = thiz->tail = NULL;
    thiz->count = 0;

    return thiz;
}

WorkItem* work_item_create(void* data, work_item_destroy_func destroy) {
    WorkItem* item = malloc(sizeof(WorkItem));
    item->data = data;
    item->destroy = destroy;
    item->next = NULL;
    return item;
}

static void work_item_destory(WorkItem* item) {
    free(item);
}

WorkItemQueue* work_item_queue_add(WorkItemQueue* thiz, void *data, work_item_destroy_func destroy) {
    pthread_mutex_lock(&thiz->locker);

    WorkItem *item = work_item_create(data, destroy);
    if(thiz->tail == NULL) {
        //empty queue
        thiz->head = thiz->tail = item;
    } else {
        //append
        WorkItem* tail = thiz->tail;
        tail->next = item;
        thiz->tail = item;
    }
    thiz->count++;

    pthread_mutex_unlock(&thiz->locker);

    return thiz;
}

int work_item_queue_get_count(WorkItemQueue* thiz) {
    return thiz->count;
}

void* work_item_queue_peek(WorkItemQueue* thiz) {
    if(thiz == NULL || thiz->head == NULL) {
        return NULL;
    }

    return thiz->head->data;
}

void* work_item_queue_remove(WorkItemQueue* thiz) {
    if(thiz == NULL || thiz->head == NULL) {
        return NULL;
    }
    pthread_mutex_lock(&thiz->locker);
    WorkItem* head = thiz->head;
    if(thiz->tail == thiz->head) {
        //only one item
        thiz->tail = thiz->head = NULL;
    } else {
        thiz->head = head->next;
    }
    thiz->count--;
    void* data = head->data;
    work_item_destory(head);

    pthread_mutex_unlock(&thiz->locker);

    return data;
}

void work_item_queue_foreach(WorkItemQueue *thiz, work_item_queue_foreach_func foreach_func, void* ctx) {
    if(thiz == NULL || foreach_func == NULL) {
        return;
    }
    pthread_mutex_lock(&thiz->locker);
    WorkItem* head = thiz->head;
    while (head != NULL) {
        void* data = head->data;
        foreach_func(data, ctx);
        WorkItem *head_next = head->next;
        head = head_next;
    }
    pthread_mutex_unlock(&thiz->locker);
}

void work_item_queue_destroy(WorkItemQueue* thiz) {
    pthread_mutex_lock(&thiz->locker);
    WorkItem* head = thiz->head;
    while (head != NULL) {
        void* data = head->data;
        if(head->destroy) {
            head->destroy(data);
        }
        WorkItem *head_next = head->next;
        work_item_destory(head);
        head = head_next;
    }
    thiz->head = thiz->tail = NULL;
    pthread_mutex_unlock(&thiz->locker);

    free(thiz);
}

#ifdef __TEST_WORK_ITEM_QUEUE__
#include "work_data.h"
void* my_work_handler(void* data, void* ctx) {
    app_sys_debug("work handler data: %d", data);
    return NULL;
}

int main() {
    WorkItemQueue* queue = work_item_queue_create();
    int data_size = 200;
    WorkData **work_data_array = (WorkData **) malloc(data_size * sizeof(WorkData *));
    for (int i = 0; i < data_size; i++) {
        work_data_array[i] = work_data_create((void *)i, my_work_handler, NULL, NULL);
        work_item_queue_add(queue, work_data_array[i], NULL);
    }
    int count = work_item_queue_get_count(queue);
    app_sys_debug("after add current count: %d", count);
    data_size=300;
    while (data_size--) {
        WorkData* work_data = (WorkData *)work_item_queue_remove(queue);
        if(work_data != NULL) {
            work_data_handle(work_data);
            work_data_destroy(work_data);
        }
    }
    count = work_item_queue_get_count(queue);
    app_sys_debug("after remove current count: %d", count);
    work_item_queue_destroy(queue);
    free(work_data_array);
 }
#endif

